#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <glui.h>
#include "Tool.h"
#include "include.h"
#include "Vertex.h"
#include "VertexList.h"
#include "ClosedObject.h"
#include "PinnedObject.h"
#include "RotatingObject.h"
#include "TranslatingObject.h"
#include "ConditionalTranslatingObject.h"
#include "PressureObject.h"
#include "CharacterObject.h"
#include "NavigationEdgeList.h"

Tool::Tool(TextureManager * textureManager,Mouse * mouse, MaterialList * materialList)
{
	this->textureManager = textureManager;
	currentToolId = EDIT_TOOL_ID;
	this->mouse = mouse;
	this->materialList = materialList;
	currentMaterialNode = NULL;
	if(this->materialList)
		currentMaterialNode = materialList->head;
		
	dragGroup = new VertexList();
	object = NULL;
	currentMode = (int*)malloc(sizeof(int) * NUMBER_TOOLS);
	numModes = (int*)malloc(sizeof(int) * NUMBER_TOOLS);
	//int modes[NUMBER_TOOLS] = MAX_MODES;
	//memcpy(numModes, modes, sizeof(int)*NUMBER_TOOLS);

	numModes[EDIT_TOOL_ID] = 4;
	numModes[CREATE_OBJECT_TOOL_ID] = 7;
	numModes[EDIT_TEXTURE_ID] = 2;
	
	for(int i=0; i < NUMBER_TOOLS; i++)
	{
		currentMode[i] = 0;
	}
}
Tool::~Tool()
{
	if(currentMode)
	{
		delete(currentMode);
		currentMode = NULL;
	}
	if(numModes)
	{
		delete(numModes);
		numModes = NULL;
	}
}
char * Tool::GetModeName()
{
	printf("GetModeName");
	switch(currentToolId)
	{
		case EDIT_TEXTURE_ID:
			switch(currentMode[currentToolId])
			{
				case SET_TEXTURE:
					return "Set";
				break;
				case EDIT_ROTATE:
					return "Rotate";
				break;
			}
			break;
		case EDIT_TOOL_ID:
			switch(currentMode[currentToolId])
			{
				case EDIT_VERTEX:
					return "Vertex";
					break;
				case EDIT_ROTATE:
					return "Rotate";
					break;
				case EDIT_EXPAND:
					return "Expand";
					break;
				case EDIT_PROPERTIES:
					return "Properties";
					break;
			}
		break;
		case CREATE_OBJECT_TOOL_ID:
			switch(currentMode[currentToolId])
			{
				case 0:
					return "Base";
					break;
				case 1:
					return "Pinned";
					break;
				case 2:
					return "Rotating";
					break;	
				case 3:
					return "Translating";
					break;
				case 4:
					return "Cond. Trans.";
					break;
				case 5:
					return "Pressure";
					break;
				case 6:
					return "Character";
					break;
			}
		break;
	}
	return "Error";
}
void Tool::NextMode()
{
	currentMode[currentToolId] = (currentMode[currentToolId]+1)%numModes[currentToolId];	
}
void Tool::LastMode()
{
	currentMode[currentToolId] = (currentMode[currentToolId]-1);
	if(currentMode[currentToolId] < 0)
	{
		currentMode[currentToolId] = numModes[currentToolId]-1;
	}	
}
void Tool::NextTool()
{
	if(dragGroup->GetLength() <= 0)
		currentToolId = (currentToolId+1)%NUMBER_TOOLS;
}
void Tool::LastTool()
{
	if(dragGroup->GetLength() <= 0)
	{
		currentToolId--;
		if(currentToolId < 0)
			currentToolId = NUMBER_TOOLS - 1;
	}
}
void Tool::SetCurrentToolId(int toolId)
{
	currentToolId = toolId;
}
int Tool::GetCurrentToolId()
{
	return currentToolId;
}
void Tool::Think(double elapsedTime)
{
	if(object)
		object->HideProperties();
	switch(currentToolId)
	{
		case EDIT_TEXTURE_ID:
		{
			switch(currentMode[currentToolId])
			{
				case EDIT_ROTATE:
				{
					List<Vertex> * search = dragGroup->head;
					Vector2D delta = mouse->GetDeltaPosition();
					if(object && search)
					{	
						Vector2D posToPoint = !(texRotationPosition - object->GetRotationPoint());//!(search->payload->GetPosition() - object->GetRotationPoint());
						texRotationPosition = mouse->GetWorldPosition();
						Vector2D posToNewPoint = !(texRotationPosition - object->GetRotationPoint());
						
						if((posToPoint | posToNewPoint) > 0.00001)
						{
							//printf("Vector1 = (%f, %f)\n",posToPoint.x, posToPoint.y);
							//printf("Vector2 = (%f, %f)\n",posToNewPoint.x, posToNewPoint.y);
							float dp = posToPoint.dot(posToNewPoint);
							if(dp < 1 && dp > 0)
							{
								float angle = acos(dp);
								printf("Rotaing by %f\n", angle);
								float cross = posToPoint.x*posToNewPoint.y - posToPoint.y*posToNewPoint.x;
								if(cross < 0)
									object->RotateTexture(angle);
								else
									object->RotateTexture(-angle);
							}
						}
					}
				}
				break;
			}
		}
		break;
		case EDIT_TOOL_ID:
		
			switch(currentMode[currentToolId])
			{
			case EDIT_VERTEX:
			{
				List<Vertex> * search = dragGroup->head;
				Vector2D delta = mouse->GetDeltaPosition();
				while(search)
				{	
					if(search->payload)
					{
						search->payload->SetPosition(search->payload->GetPosition() + delta);	
					}
					search = search->next;
				}
				if(object)
					object->CalculatePosition();
			}
			break;
			case EDIT_ROTATE:
			{
				List<Vertex> * search = dragGroup->head;
				Vector2D delta = mouse->GetDeltaPosition();
				if(object && search)
				{	
					Vector2D posToPoint = !(search->payload->GetPosition() - object->GetRotationPoint());
					Vector2D posToNewPoint = !((mouse->GetWorldPosition()) - object->GetRotationPoint());
					
					if((posToPoint | posToNewPoint) > 0.00001)
					{
						printf("Vector1 = (%f, %f)\n",posToPoint.x, posToPoint.y);
						printf("Vector2 = (%f, %f)\n",posToNewPoint.x, posToNewPoint.y);
						float dp = posToPoint.dot(posToNewPoint);
						if(dp < 1 && dp > 0)
						{
							float angle = acos(dp);
							printf("Rotaing by %f\n", angle);
							float cross = posToPoint.x*posToNewPoint.y - posToPoint.y*posToNewPoint.x;
							if(cross < 0)
								object->Rotate(-angle);
							else
								object->Rotate(angle);
						}
					}
				}
			}
			break;
			case EDIT_EXPAND:
			{
				List<Vertex> * search = dragGroup->head;
				Vector2D delta = mouse->GetDeltaPosition();
				if(object && search)
				{	
					float origDist = search->payload->GetPosition() | object->GetRotationPoint();
					float newDist = mouse->GetWorldPosition() | object->GetRotationPoint();
					
					if(origDist != newDist && newDist > 0 && origDist > 0)
					{
							
						object->Expand(newDist/origDist);
					}	
				}
			}
			break;
			case EDIT_PROPERTIES:
			{
				if(object)
					object->ViewProperties();
			}
			break;
			
			}
		break;
	}
	
}
void Tool::LiftPrimaryFunction()
{
	if(currentToolId == EDIT_TOOL_ID || currentToolId == EDIT_TEXTURE_ID)
	{
		if(dragVertex)
			dragVertex = NULL;
		dragGroup->ClearList();
	}
}
void Tool::PrimaryFunction(ObjectList * objectList)
{
	printf("Primary function\n");
	Object * nextObject = NULL;
	Object * prevObject = object;
	if(object)
		object->HideProperties();
	switch(currentToolId)
	{
		case EDIT_TEXTURE_ID:
		{
			objectList->HoverCheck(mouse);
			
			nextObject = objectList->GetHoverObject(mouse);
			if(nextObject)
				object = nextObject;
			switch(currentMode[currentToolId])
			{
				case SET_TEXTURE:
				{
					if(object && currentMaterialNode && currentMaterialNode->payload)
					{
						object->SetMaterial(currentMaterialNode->payload);
					}
				}
				break;
				case EDIT_ROTATE:
				{
					texRotationPosition = mouse->GetWorldPosition();
					Vertex * dragVertex = object->GetHover(mouse);
					if(dragVertex)
					{
						dragGroup->Add(dragVertex);
					}
				}
				break;
			}
		}
		break;
		case EDIT_TOOL_ID:
		
			printf("Edit\n");
			objectList->HoverCheck(mouse);
			
			nextObject = objectList->GetHoverObject(mouse);
			if(nextObject)
				object = nextObject;
			if(object && object == prevObject)
			{
				switch(currentMode[currentToolId])
				{
					case EDIT_VERTEX:
					{
						
						Vertex * dragVertex = object->GetHover(mouse);
						if(dragVertex)
						{
							dragGroup->Add(dragVertex);
						}
						else
						{
							object->GetHoverGroup(mouse, dragGroup);
							
							if(dragGroup->GetLength() <= 0)
							{
								object->AddVertex(new Vertex(mouse->GetWorldPosition()));
							}
						}
		
					}
					break;
					case EDIT_ROTATE:
					case EDIT_EXPAND:
					{
						Vertex * dragVertex = object->GetHover(mouse);
						if(dragVertex)
						{
							dragGroup->Add(dragVertex);
						}
					}
					break;
				}
			}
			else if(object && prevObject && object->GetParent() && object->GetParent() == prevObject->GetParent())
			{
				switch(currentMode[currentToolId])
				{
					case EDIT_VERTEX:
					{
						Vertex * dragVertex = object->GetHover(mouse);
						if(dragVertex)
						{
							prevObject->AddVertex(dragVertex);
							dragGroup->Add(dragVertex);
						}
					}
					break;
				}
			}
		break;
		case CREATE_OBJECT_TOOL_ID:
			switch(currentMode[currentToolId])
			{
				case CREATE_BASE_OBJECT_ID:
					objectList->Add(new ClosedObject(textureManager, Vertex(mouse->GetWorldPosition())));
					break;
				case CREATE_PINNED_OBJECT_ID:
					objectList->Add(new PinnedObject(textureManager, Vertex(mouse->GetWorldPosition())));
					break;
				case CREATE_ROTATING_OBJECT_ID:
					objectList->Add(new RotatingObject(textureManager, Vertex(mouse->GetWorldPosition())));
					break;
				case CREATE_TRANSLATING_OBJECT_ID:
				{
					TranslatingObject * trans = new TranslatingObject(textureManager, Vertex(mouse->GetWorldPosition()));
					objectList->Add(trans);
					objectList->Add(trans->GetPath());
				}
				break;
				case CREATE_CONDITIONAL_TRANSLATING_OBJECT_ID:
				{
					ConditionalTranslatingObject * trans = new ConditionalTranslatingObject(textureManager, Vertex(mouse->GetWorldPosition()));
					objectList->Add(trans);
					objectList->Add(trans->GetPath());
				}
				break;
				case CREATE_PRESSURE_OBJECT_ID:
				{
					PressureObject * pressure = new PressureObject(textureManager, Vertex(mouse->GetWorldPosition()));
					objectList->Add(pressure);
					objectList->Add(pressure->GetPath());
				}
				break;
				case CREATE_CHARACTER_OBJECT_ID:
				{
					CharacterObject * character = new CharacterObject(objectList, textureManager, Vertex(mouse->GetWorldPosition()));
					objectList->Add(character);
					NavigationEdgeList * list = character->GetNavigationEdgeList();
					if(list)
					{
						List<NavigationEdge> * search = list->head;
						printf("list len = %d\n", list->GetLength());
						while(search)
						{
							if(search->payload)
							{
								printf("Adding one\n");
								objectList->Add(search->payload);
								search->payload->SetParent(character);
							}
							search = search->next;
						}
					}
					printf("Done adding character\n");
				}
			}
		break;
	}
}
void Tool::SecondaryFunction()	
{
	if(currentToolId == EDIT_TOOL_ID)
	{
		if(object)
		{
			Vertex * deleteVertex = object->GetHover(mouse);
			if(deleteVertex)
			{
				object->RemoveVertex(deleteVertex);
			}
			else
			{
				VertexList segment;
				object->GetHoverGroup(mouse, &segment);
				
				if(segment.GetLength() == 2)
				{
					Vector2D mousePos = mouse->GetWorldPosition();
					Vector2D leftPos = segment.head->payload->GetPosition();
					Vector2D rightPos = segment.head->next->payload->GetPosition();
					
					Vector2D closestPoint = Vector2D::ClosestPointOnSegment(leftPos, rightPos, mousePos);
					object->AddVertexBefore(new Vertex(closestPoint), segment.head->payload);
				}
			}
		}
	}
}
bool Tool::HasSelectedObject()
{
	return object != NULL;
}
void Tool::CheckSelectedObjectHover()
{
	if(object)
		object->CheckHover(mouse);
}
void Tool::NextParameter()
{
	switch(currentToolId)
	{
		case EDIT_TEXTURE_ID:
		{
			printf("Trying\n");
			printf("materialList length = %d\n", materialList->GetLength());
			if(currentMaterialNode)
			{
				printf("Here1\n");
				currentMaterialNode = currentMaterialNode->next;
			}
			if(!currentMaterialNode)
			{
				printf("Here2\n");
				currentMaterialNode = materialList->head;
			}
				
			printf("Setting current material to %s\n", currentMaterialNode->payload->name);
		}
		break;
	}
}
void Tool::LastParameter()
{
	printf("Last\n");
	switch(currentToolId)
	{
		case EDIT_TEXTURE_ID:
		{
			currentMaterialNode = currentMaterialNode->prev;
			if(!currentMaterialNode)
				currentMaterialNode = materialList->tail;
		}
		break;
	}
}
char * Tool::GetParameterName()
{
	switch(currentToolId)
	{
		case EDIT_TEXTURE_ID:
		{
			if(currentMaterialNode && currentMaterialNode->payload)
			{
				return currentMaterialNode->payload->name;
			}
		}
		break;
	}
	return "";
}
